package com.cardone.persistent.builder;

import lombok.*;
import lombok.experimental.*;
import org.apache.commons.lang3.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.*;
import org.springframework.util.*;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

/**
 * 默认映射生成器
 *
 * @author yaohaitao
 */
@Getter
@Setter
@Accessors(chain = true)
public class ModelDefault extends HashMap<String, Object> implements Model {
    /**
     * 序列号
     */
    private static final long serialVersionUID = 1L;

    /**
     * 默认键值前缀名称
     */
    private String defaultKeyPrefix;

    /**
     * 默认键值后缀名称
     */
    private String defaultKeySuffix;

    /**
     * 默认间隔
     */
    private String defaultSpace;

    private void doWith(final Field field, final ModelArgs modelArgs) throws IllegalArgumentException, IllegalAccessException {
        final String fieldName = field.getName();

        if (modelArgs.getO() == null) {
            if (BooleanUtils.isTrue(modelArgs.getIsSimple())) {
                ModelDefault.this.put(new String[]{fieldName}, null);

                return;
            }

            ModelDefault.this.putTrueExtend(modelArgs.getKeyPrefix(), fieldName);

            return;
        }

        final Object value = FieldUtils.readField(field, modelArgs.getO(), true);

        if (BooleanUtils.isTrue(modelArgs.getSkipNullValue())) {
            if (value == null) {
                return;
            }

            if ((value instanceof String) && StringUtils.isBlank(String.valueOf(value))) {
                return;
            }
        }

        if (BooleanUtils.isTrue(modelArgs.getIsSimple())) {
            ModelDefault.this.put(new String[]{fieldName}, value);

            return;
        }

        ModelDefault.this.putExtend(modelArgs.getKeyPrefix(), fieldName, modelArgs.getKeySuffix(), value);
    }

    private boolean matches(final Field field, final ModelArgs modelArgs) {
        if (ReflectionUtils.isPublicStaticFinal(field)) {
            return false;
        }

        final String fieldName = field.getName();

        if (ArrayUtils.contains(modelArgs.getIgnoreProperties(), fieldName)) {
            return false;
        }

        if (ArrayUtils.isNotEmpty(modelArgs.getUseProperties())) {
            return ArrayUtils.contains(modelArgs.getUseProperties(), fieldName);
        }

        if (!CollectionUtils.isEmpty(modelArgs.getIgnoreAnnotationClassList())) {
            for (final Class<? extends Annotation> ignoreAnnotationClass : modelArgs.getIgnoreAnnotationClassList()) {
                if (field.isAnnotationPresent(ignoreAnnotationClass)) {
                    return false;
                }
            }
        }

        if (CollectionUtils.isEmpty(modelArgs.getUseAnnotationClassList())) {
            return true;
        }

        for (final Class<? extends Annotation> useAnnotationClass : modelArgs.getUseAnnotationClassList()) {
            if (field.isAnnotationPresent(useAnnotationClass)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public Model put(final String[] keys, final Object value) {
        if (ArrayUtils.isEmpty(keys)) {
            return this;
        }

        final String key = StringUtils.join(keys, StringUtils.defaultIfBlank(this.defaultSpace, Model.Keys.defaultSpace.stringValue()));

        this.put(key, value);

        return this;
    }

    @Override
    public Model putExtend(final String keyPrefix, final Map<String, Object> kvMap, final String keySuffix) {
        if (CollectionUtils.isEmpty(kvMap)) {
            return this;
        }

        for (final Entry<String, Object> entry : kvMap.entrySet()) {
            this.putExtend(keyPrefix, entry.getKey(), keySuffix, entry.getValue());
        }

        return this;
    }

    @Override
    public Model putExtend(final String keyPrefix, final String keyBase, final String keySuffix, final Object value) {
        return this.putExtend(keyPrefix, new String[]{keyBase}, keySuffix, value);
    }

    @Override
    public Model putExtend(final String keyPrefix, final String[] keyBases, final String keySuffix, final Object value) {
        if (ArrayUtils.isEmpty(keyBases)) {
            return this;
        }

        final String newKeyPrefix = com.cardone.common.util.StringUtils.defaultIfBlank(keyPrefix, this.defaultKeyPrefix, Model.Keys.defaultKeyPrefix.stringValue());

        final String newKeySuffix = com.cardone.common.util.StringUtils.defaultIfBlank(keySuffix, this.defaultKeySuffix, Model.Keys.defaultKeySuffix.stringValue());

        for (final String keyBase : keyBases) {
            this.putTrue(newKeyPrefix, keyBase);

            this.put(new String[]{newKeyPrefix, keyBase, newKeySuffix}, value);
        }

        return this;
    }

    @Override
    public Model putObjectField(final ModelArgs modelArgs) {
        if (modelArgs.getC() == null) {
            if (modelArgs.getO() == null) {
                return this;
            }

            modelArgs.setC(modelArgs.getO().getClass());
        }

        ReflectionUtils.doWithFields(modelArgs.getC(), (Field field) -> ModelDefault.this.doWith(field, modelArgs), (Field field) -> ModelDefault.this.matches(field, modelArgs));

        return this;
    }

    @Override
    public Model putTrue(final String... keys) {
        this.put(keys, Boolean.TRUE.toString());

        return this;
    }

    @Override
    public Model putTrueExtend(final String keyPrefix, final String... keyBases) {
        if (ArrayUtils.isEmpty(keyBases)) {
            return this;
        }

        final String newKeyPrefix = com.cardone.common.util.StringUtils.defaultIfBlank(keyPrefix, this.defaultKeyPrefix, Model.Keys.defaultKeyPrefix.stringValue());

        for (final String keyBase : keyBases) {
            this.putTrue(newKeyPrefix, keyBase);
        }

        return this;
    }

    @Override
    public Model remove(final String... keys) {
        if (ArrayUtils.isEmpty(keys)) {
            return this;
        }

        final String key = StringUtils.join(keys, StringUtils.defaultIfBlank(this.defaultSpace, Model.Keys.defaultSpace.stringValue()));

        this.remove(key);

        return this;
    }

    @Override
    public Model removeExtend(final String keyPrefix, final String keyBase, final String keySuffix) {
        return this.removeExtend(keyPrefix, new String[]{keyBase}, keySuffix);
    }

    @Override
    public Model removeExtend(final String keyPrefix, final String[] keyBases, final String keySuffix) {
        if (ArrayUtils.isEmpty(keyBases)) {
            return this;
        }

        final String newKeyPrefix = com.cardone.common.util.StringUtils.defaultIfBlank(keyPrefix, this.defaultKeyPrefix, Model.Keys.defaultKeyPrefix.stringValue());

        final String newKeySuffix = com.cardone.common.util.StringUtils.defaultIfBlank(keySuffix, this.defaultKeySuffix, Model.Keys.defaultKeySuffix.stringValue());

        for (final String keyBase : keyBases) {
            this.remove(newKeyPrefix, keyBase);

            this.remove(newKeyPrefix, keyBase, newKeySuffix);
        }

        return this;
    }
}